/* LVGL Example project
 *
 * Basic project to test LVGL on ESP32 based projects.
 *
 * This example code is in the Public Domain (or CC0 licensed, at your option.)
 *
 * Unless required by applicable law or agreed to in writing, this
 * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied.
 */
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_freertos_hooks.h"

#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "cJSON.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"

#include "driver/gpio.h"
#include "gui_guider.h"
#include "events_init.h"

#include "http.h"
#include "wifi.h"
#include "sntp.h"

lv_ui guider_ui;
/* Littlevgl specific */
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif

#include "lvgl_helpers.h"

#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
#if defined CONFIG_LV_USE_DEMO_WIDGETS
#include "lv_examples/src/lv_demo_widgets/lv_demo_widgets.h"
#elif defined CONFIG_LV_USE_DEMO_KEYPAD_AND_ENCODER
#include "lv_examples/src/lv_demo_keypad_encoder/lv_demo_keypad_encoder.h"
#elif defined CONFIG_LV_USE_DEMO_BENCHMARK
#include "lv_examples/src/lv_demo_benchmark/lv_demo_benchmark.h"
#elif defined CONFIG_LV_USE_DEMO_STRESS
#include "lv_examples/src/lv_demo_stress/lv_demo_stress.h"
#else
#error "No demo application selected."
#endif
#endif

static char wifi_Connection = 0;

extern lv_font_t myFont1;
extern HTTP_weather_data_t HTTP_weather_data;
extern SNTP_time_data_t SNTP_Time;
/*********************
 *      DEFINES
 *********************/
#define TAG "demo"
#define LV_TICK_PERIOD_MS 1

#define BTN_TAG "ADC_Btn"
/**********************
 *  STATIC PROTOTYPES
 **********************/
static void lv_tick_task(void *arg);
static void guiTask(void *pvParameter);
static void create_demo_application(void);

static void Clock_Show_Task(void *pvParameter);

TaskHandle_t xTask_HTTP_Handle = NULL;
TaskHandle_t xTask_SNTP_Handle = NULL;
/**********************
 *   APPLICATION MAIN
 **********************/
void app_main()
{
    // Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_Connection = wifi_init_sta();
    // NOTE:成功连接
    if (wifi_Connection == 1)
    {
        /* If you want to use a task to create the graphic, you NEED to create a Pinned task
         * Otherwise there can be problem such as memory corruption and so on.
         * NOTE: When not using Wi-Fi nor Bluetooth you can pin the guiTask to core 0 */
        xTaskCreatePinnedToCore(guiTask, "gui", 4096 * 2, NULL, 0, NULL, 0);

        xTaskCreatePinnedToCore(Clock_Show_Task, "btn", 4096 * 1, NULL, 1, NULL, 0);

        xTaskCreate(&http_get_task, "http_get_task", 4096, NULL, 5, &xTask_HTTP_Handle);

        xTaskCreate(&sntp_Task, "sntp_Task", 4096, NULL, 2, &xTask_SNTP_Handle);
    }
    // NOTE:连接失败
    else if (wifi_Connection == 2)
    {
        xTaskCreatePinnedToCore(guiTask, "gui", 4096 * 2, NULL, 0, NULL, 0);
        // TODO: 加入Smart connect 功能
        while (1)
        {
            // NOTE: 错误显示
            if (guider_ui.screen_DisConnect != NULL)
            {
                lv_label_set_text_fmt(guider_ui.screen_DisConnect, "%s", "Network Disconnect");
                vTaskDelay(pdMS_TO_TICKS(3000));
                esp_restart();
            }
            vTaskDelay(pdMS_TO_TICKS(1000));
        }
    }
    // NOTE:未知情况
    else
    {
        while (1)
        {
            // NOTE: 10 sec restart
            vTaskDelay(pdMS_TO_TICKS(10000));
            esp_restart();
        }
    }
}

/* Creates a semaphore to handle concurrent call to lvgl stuff
 * If you wish to call *any* lvgl function from other threads/tasks
 * you should lock on the very same semaphore! */
SemaphoreHandle_t xGuiSemaphore;

static void guiTask(void *pvParameter)
{

    (void)pvParameter;
    xGuiSemaphore = xSemaphoreCreateMutex();

    lv_init();

    /* Initialize SPI or I2C bus used by the drivers */
    lvgl_driver_init();

    lv_color_t *buf1 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf1 != NULL);

    /* Use double buffered when not working with monochrome displays */
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
    lv_color_t *buf2 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
    assert(buf2 != NULL);
#else
    static lv_color_t *buf2 = NULL;
#endif

    static lv_disp_buf_t disp_buf;

    uint32_t size_in_px = DISP_BUF_SIZE;

#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306

    /* Actual size in pixels, not bytes. */
    size_in_px *= 8;
#endif

    /* Initialize the working buffer depending on the selected display.
     * NOTE: buf2 == NULL when using monochrome displays. */
    lv_disp_buf_init(&disp_buf, buf1, buf2, size_in_px);

    lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.flush_cb = disp_driver_flush;

    /* When using a monochrome display we need to register the callbacks:
     * - rounder_cb
     * - set_px_cb */
#ifdef CONFIG_LV_TFT_DISPLAY_MONOCHROME
    disp_drv.rounder_cb = disp_driver_rounder;
    disp_drv.set_px_cb = disp_driver_set_px;
#endif

    disp_drv.buffer = &disp_buf;
    lv_disp_drv_register(&disp_drv);

    /* Register an input device when enabled on the menuconfig */
#if CONFIG_LV_TOUCH_CONTROLLER != TOUCH_CONTROLLER_NONE
    lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.read_cb = touch_driver_read;
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    lv_indev_drv_register(&indev_drv);
#endif

    /* Create and start a periodic timer interrupt to call lv_tick_inc */
    const esp_timer_create_args_t periodic_timer_args = {
        .callback = &lv_tick_task,
        .name = "periodic_gui"};
    esp_timer_handle_t periodic_timer;
    ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
    ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 1000));

    /* Create the demo application */
    // create_demo_application();
    //  NOTE: 启动GUI_Guider
    setup_ui(&guider_ui);
    events_init(&guider_ui);

    while (1)
    {
        /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
        vTaskDelay(pdMS_TO_TICKS(10));

        /* Try to take the semaphore, call lvgl related function on success */
        if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY))
        {
            lv_task_handler();
            xSemaphoreGive(xGuiSemaphore);
        }
    }

    /* A task should NEVER return */
    free(buf1);
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
    free(buf2);
#endif
    vTaskDelete(NULL);
}

static void create_demo_application(void)
{
    /* When using a monochrome display we only show "Hello World" centered on the
     * screen */
#if defined CONFIG_LV_TFT_DISPLAY_MONOCHROME || \
    defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_ST7735S

    /* use a pretty small demo for monochrome displays */
    /* Get the current screen  */
    lv_obj_t *scr = lv_disp_get_scr_act(NULL);

    /*Create a Label on the currently active screen*/
    lv_obj_t *label1 = lv_label_create(scr, NULL);

    /*Modify the Label's text*/
    lv_label_set_text(label1, "Hello\nworld");

    /* Align the Label to the center
     * NULL means align on parent (which is the screen now)
     * 0, 0 at the end means an x, y offset after alignment*/
    lv_obj_align(label1, NULL, LV_ALIGN_CENTER, 0, 0);
#else
    /* Otherwise we show the selected demo */

#if defined CONFIG_LV_USE_DEMO_WIDGETS
    lv_demo_widgets();
#elif defined CONFIG_LV_USE_DEMO_KEYPAD_AND_ENCODER
    lv_demo_keypad_encoder();
#elif defined CONFIG_LV_USE_DEMO_BENCHMARK
    lv_demo_benchmark();
#elif defined CONFIG_LV_USE_DEMO_STRESS
    lv_demo_stress();
#else
#error "No demo application selected."
#endif
#endif
}

static void lv_tick_task(void *arg)
{
    (void)arg;

    lv_tick_inc(LV_TICK_PERIOD_MS);
}

static void Clock_Show_Task(void *pvParameter)
{
    static lv_style_t label_style = {0}; // style 必须要为static
    char Min_L_counter = 0;
    char Min_H_counter = 0;
    char Hour_L_counter = 0;
    char Hour_H_counter = 0;
    while (1)
    {
        ESP_LOGD(BTN_TAG, "Task is Running\r\n");
        if (guider_ui.screen_Min_L != NULL)
        {
#if (0)
            Min_L_counter++;
            if (Min_L_counter >= 10)
            {
                Min_L_counter = 0;
                Min_H_counter++;
                if (Min_H_counter >= 6)
                {
                    Min_L_counter = 0;
                    Min_H_counter = 0;
                    Hour_L_counter++;
                    if (Hour_L_counter >= 10)
                    {
                        Hour_L_counter = 0;
                        Hour_H_counter++;
                    }
                    if (((Hour_H_counter * 10) + Hour_L_counter) >= 24)
                    {
                        Min_L_counter = 0;
                        Min_H_counter = 0;
                        Hour_L_counter = 0;
                        Hour_H_counter = 0;
                    }
                }
            }

#else
            lv_label_set_text_fmt(guider_ui.screen_DisConnect, "%s", "Network Connect");

            // NOTE: 时钟显示 
            Min_L_counter = atoi(SNTP_Time.Minute) % 10;
            Min_H_counter = atoi(SNTP_Time.Minute) / 10;
            Hour_L_counter = atoi(SNTP_Time.Hour) % 10;
            Hour_H_counter = atoi(SNTP_Time.Hour) / 10;
#endif
            lv_label_set_text_fmt(guider_ui.screen_Min_L, "%d", Min_L_counter);
            lv_label_set_text_fmt(guider_ui.screen_Min_H, "%d", Min_H_counter);
            lv_label_set_text_fmt(guider_ui.screen_Hour_L, "%d", Hour_L_counter);
            lv_label_set_text_fmt(guider_ui.screen_Hour_H, "%d", Hour_H_counter);

            // 显示天气数据
            lv_label_set_recolor(guider_ui.screen_City, true);
            lv_label_set_recolor(guider_ui.screen_Template, true);
            lv_label_set_recolor(guider_ui.screen_weather, true);
            lv_label_set_recolor(guider_ui.screen_humidity, true);
            lv_label_set_recolor(guider_ui.screen_wind_direction, true);

            // 增加样式，更改字体
            lv_label_set_text_fmt(guider_ui.screen_City, "%s", HTTP_weather_data.City);
            lv_label_set_text_fmt(guider_ui.screen_Template, "%s", HTTP_weather_data.Temperature);
            lv_label_set_text_fmt(guider_ui.screen_humidity, "%s", HTTP_weather_data.Humidity);
            lv_label_set_text_fmt(guider_ui.screen_weather, "%s", HTTP_weather_data.Weather);
            lv_label_set_text_fmt(guider_ui.screen_wind_direction, "%s", HTTP_weather_data.Wind_Direction);

            //设置样式字体
            lv_style_set_text_font(&label_style, LV_STATE_DEFAULT, &myFont1);

            //应用样式
            lv_obj_add_style(guider_ui.screen_City, LV_LABEL_PART_MAIN, &label_style);
            lv_obj_add_style(guider_ui.screen_Template, LV_LABEL_PART_MAIN, &label_style);
            lv_obj_add_style(guider_ui.screen_humidity, LV_LABEL_PART_MAIN, &label_style);
            lv_obj_add_style(guider_ui.screen_weather, LV_LABEL_PART_MAIN, &label_style);
            lv_obj_add_style(guider_ui.screen_wind_direction, LV_LABEL_PART_MAIN, &label_style);
        }
        /* Delay 1 tick (assumes FreeRTOS tick is 10ms */
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
    vTaskDelete(NULL);
}
